package com.zygh.lz.util;

import cn.hutool.poi.excel.ExcelWriter;
import org.apache.poi.ss.usermodel.*;
import org.springframework.scheduling.annotation.Async;

import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 距离计算 工具类  两点之间
 */
public class DistanceUtil {


    public static double PI = Math.PI;

    private static double getRad(double d) {
        return d * PI / 180.0;
    }

    private static double EARTH_RADIUS = 6378137;
    //private static double EARTH_SEA = 1.852;海里

    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }


    /**
     * 计算两经纬度之间的距离，单位是m，用于直线测量使用
     * approx distance between two points on earth ellipsoid
     * 参考椭球半径 6378137.0    郑州海拔平均108
     * double EARTH_RADIUS_AND_ALTITUDE 参考椭球半径+平均海拔
     * <p>
     * double lat1, double lng1, 起始点经纬度
     * double lat2, double lng2  终止点经纬度
     */
    public static double getLineDistance(double lat1, double lng1, double lat2, double lng2, double EARTH_RADIUS_AND_ALTITUDE) {

        double f = getRad((lat1 + lat2) / 2);
        double g = getRad((lat1 - lat2) / 2);
        double l = getRad((lng1 - lng2) / 2);
        double sg = Math.sin(g);
        double sl = Math.sin(l);
        double sf = Math.sin(f);

        double s, c, w, r, d, h1, h2;
        double a = EARTH_RADIUS_AND_ALTITUDE;
        double fl = 1 / 298.257223565;
        sg = sg * sg;
        sl = sl * sl;
        sf = sf * sf;

        s = sg * (1 - sl) + (1 - sf) * sl;
        c = (1 - sg) * (1 - sl) + sf * sl;

        w = Math.atan(Math.sqrt(s / c));
        r = Math.sqrt(s * c) / w;
        d = 2 * w * a;
        h1 = (3 * r - 1) / 2 / c;
        h2 = (3 * r + 1) / 2 / s;

        return d * (1 + fl * (h1 * sf * (1 - sg) - h2 * (1 - sf) * sg));
    }


    /**
     * 计算圆曲线弧长，单位是m，用于圆曲线测量使用
     * approx distance between two points on earth ellipsoid
     * double lat1, double lng1, 起始点经纬度
     * double lat2, double lng2  终止点经纬度
     * double R     圆曲线直径
     * <p>
     * 原理：
     * 在进行曲线测量时，输入圆曲线的半径R，获取圆曲线测量开始的点作为起始点，
     * 将此半径下实时运动的圆弧长设为L1,周长设为L,设运动过程中的圆心角是a,运动过程中运动点和起始点的弦长是B
     * 根据公式：
     * L(周长)=2πR^2,
     * L1(弧长)=2Rsina，
     * Sin(a/2)=(B/2)/R，a（当前角度）=2arcSin((B/2)/R),
     * 当a=2π时，L=L1
     * 则L1=L*(a/2π)
     * 即可实时的求出当前点行进的弧长距离
     */
    public static double getCircleDistance(double lat1, double lng1, double lat2, double lng2, double R, double EARTH_RADIUS_AND_ALTITUDE) {
        double L = 0.0;//圆的周长
        double L1 = 0.0;//实时运动的弧长
        double a = 0.0;//设运动过程中的圆心角是a
        double B = 0.0;//运动过程中运动点和起始点的弦长是B
        L = 2 * PI * R;
        B = getLineDistance(lat1, lng1, lat2, lng2, EARTH_RADIUS_AND_ALTITUDE);
        if (R < B) {
            B = R;
        }
        a = 2 * Math.asin(((B / 2) / R));
        L1 = L * (a / (2 * PI));
        return L1;
    }


    /**
     * 计算民警巡查里程 单位（里程）
     *
     * @param gps
     * @return
     */
    public static String jq(String gps) {
        double distance = 0.0;
        int num = 0;
        int nub = 0;
        String[] split = gps.split(",");
        String[] gpsx = new String[split.length / 2];
        String[] gpsy = new String[split.length / 2];
        for (int k = 0; k < split.length; k++) {
            if (k % 2 == 0) {
                gpsx[num] = split[k];
                num++;

            } else {
                gpsy[nub] = split[k];
                nub++;
            }
        }

        for (int t = 0; t < gpsx.length; t++) {
            double lineDistance = 0.0;
            if (t + 1 < gpsx.length) {
                double lat1 = Double.valueOf(gpsx[t]);
                double lng1 = Double.valueOf(gpsy[t]);
                double lat2 = Double.valueOf(gpsx[t + 1]);
                double lng2 = Double.valueOf(gpsy[t + 1]);
                lineDistance = getLineDistance(lat1, lng1, lat2, lng2, 6378245);
                Double a = Double.NaN;
                if (!a.equals(lineDistance)) {
                    distance += lineDistance;
                }
            }

        }
        //得到长度单位是米  换算成里程除以 500  换算成公里除以1000
        distance = distance / 1000;
        DecimalFormat dub = new DecimalFormat("#0.00");
        return dub.format(distance);
    }

    public static Map<String, double[]> getBorder(String gps) {
        int num = 0;
        int nub = 0;
        String[] split = gps.split(",");
        double[] gpsx = new double[split.length / 2];
        double[] gpsy = new double[split.length / 2];
        for (int k = 0; k < split.length; k++) {
            if (k % 2 == 0) {
                gpsx[num] = Double.parseDouble(split[k]);
                num++;
            } else {
                gpsy[nub] = Double.parseDouble(split[k]);
                nub++;
            }
        }
        Map<String, double[]> map = new HashMap<>();
        map.put("gpsx", gpsx);
        map.put("gpsy", gpsy);
        return map;
    }

    public static void main(String[] args) {
        String gps = "113.64712661109348,34.73772322332276,113.63433179684702,34.73759970817776,113.62088406019978,34.73761389002895,113.60587996679904,34.73763718546325,113.59050027156003,34.7376026185975,113.57653921817436,34.73755969509899,113.56307988958027,34.73770351390264,113.55055070074418,34.739300821954934,113.53907539608751,34.73352764452956,113.52611058312776,34.73100609451746";
        System.out.println(jq(gps));
    }


    /**
     * lat1 ，lng1 ，raduis分别为圆的纬度，经度和半径
     * lat ，lng为点的纬度，经度
     * 判断坐标点是否在指定坐标300米范围内
     *
     * @param lat1
     * @param lng1
     * @param lat
     * @param lng
     * @param raduis
     * @return
     */
    public Boolean yuan(double lat1, double lng1, double lat, double lng, double raduis) {
        double R = 6378137.0;
        double dLat = (lat1 - lat) * Math.PI / 180;
        double dLng = (lng1 - lng) * Math.PI / 180;

        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat * Math.PI / 180) * Math.cos(lat1 * Math.PI / 180) * Math.sin(dLng / 2) * Math.sin(dLng / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        double d = R * c;
        double dis = Math.round(d);

        if (dis <= raduis) {
            //点在圆内
            return true;
        } else {
            //点不在圆内
            return false;
        }

    }

    /**
     * 将两个经纬度坐标转化成距离（米）
     *
     * @param X       维度
     * @param Y       经度
     * @param circleX 中心点维度
     * @param circleY 中心点经度
     * @param r       半径 （单位：米）
     */
    public static boolean distencePC(double X, double Y, double circleX, double circleY, double r) {
        double a = X * Math.PI / 180.0 - circleX * Math.PI / 180.0;
        double b = Y * Math.PI / 180.0 - circleY * Math.PI / 180.0;
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(X * Math.PI / 180.0)
                * Math.cos(circleX * Math.PI / 180.0)
                * Math.pow(Math.sin(b / 2), 2)));
        s = s * 6378.137 * 1000;
        s = Math.round(s);
        if (s > r) return false;
        return true;
    }

    /**
     * @param lat1   纬度（需判断点的纬度）
     * @param lat2   纬度（框选的纬度）
     * @param lng1   经度（需判断点的经度）
     * @param lng2   经度（框选的经度）
     * @param radius （单位：公里）判断一个点是否在圆形区域内,比较坐标点与圆心的距离是否小于半径
     */
    public static boolean isInCircle(double lng1, double lat1, double lng2, double lat2, double radius) {
        double distance = getDistance(lat1, lng1, lat2, lng2);
        if (distance > radius) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 通过经纬度获取距离(单位：米)
     *
     * @param lat1 纬度1
     * @param lng1 经度1
     * @param lat2 纬度2
     * @param lng2 经度2
     * @return 距离
     */
    public static double getDistance(double lat1, double lng1, double lat2,
                                     double lng2) {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
                Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000d) / 10000d;
        //double len=s/EARTH_SEA;
        //s = s / 1000;
        return s;
    }


    /**
     * 合并Map集合
     */
    public static List<Map<String, Object>> merge(List<Map<String, Object>> m1, List<Map<String, Object>> m2) {
        if (m1 == null) return m2;
        if (m2 == null) return m1;

        m1.addAll(m2);
        Set<String> set = new HashSet<>();
        return m1.stream()
                .collect(Collectors.groupingBy(o -> {
                    //暂存所有key
                    set.addAll(o.keySet());
                    //按a_id分组
                    return o.get("id");
                })).entrySet().stream().map(o -> {
                    //合并
                    Map<String, Object> map = o.getValue().stream().flatMap(m -> {
                        return m.entrySet().stream();
                    }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b));

                    //为没有的key赋值0
                    set.stream().forEach(k -> {
                        if (!map.containsKey(k)) {
                            map.put(k, 0);
                        }
                    });

                    return map;
                }).collect(Collectors.toList());

    }

    /**
     * 自定义设置单元格样式
     *
     * @param writer hutool-Excel写入器
     * @param x      x_坐标
     * @param y      y_坐标
     * @param isTrue true ->设置为灰色;  false ->设置为黄色;
     */
    public static void setCellStyle(ExcelWriter writer, int x, int y, Boolean isTrue) {
        CellStyle cellStyle = writer.createCellStyle(x, y);
        // 顶边栏
        cellStyle.setBorderTop(BorderStyle.THIN);
        //cellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
        // 右边栏
        cellStyle.setBorderRight(BorderStyle.THIN);
        //cellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
        // 底边栏
        cellStyle.setBorderBottom(BorderStyle.THIN);
        //cellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
        // 左边栏
        cellStyle.setBorderLeft(BorderStyle.THIN);
        //cellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
        // 填充前景色(两个一起使用)
        cellStyle.setFillForegroundColor(isTrue ? IndexedColors.GREY_25_PERCENT.getIndex() : IndexedColors.YELLOW.getIndex());
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
    }
}
